
#include "sqlite3.h"
#include "sqlite3x.hpp"

namespace sqlite3x {

    sqlite3_command::sqlite3_command(sqlite3_connection &con, const char *sql) :
con(con),
refs(0),
bind_index(1)
{
    const char *tail=NULL;
    if(sqlite3_prepare(con.db, sql, -1, &this->stmt, &tail)!=SQLITE_OK)
        throw database_error(con);

    this->argc=sqlite3_column_count(this->stmt);
}

sqlite3_command::sqlite3_command(sqlite3_connection &con, const wchar_t *sql) :
con(con),
refs(0),
bind_index(1)
{
    const wchar_t *tail=NULL;
    if(sqlite3_prepare16(con.db, sql, -1, &this->stmt, (const void**)&tail)!=SQLITE_OK)
        throw database_error(con);

    this->argc=sqlite3_column_count(this->stmt);
}

sqlite3_command::sqlite3_command(sqlite3_connection &con, const std::string &sql) :
con(con),
refs(0),
bind_index(1)
{
    const char *tail=NULL;
    if(sqlite3_prepare(con.db, sql.data(), (int)sql.length(), &this->stmt, &tail)!=SQLITE_OK)
        throw database_error(con);

    this->argc=sqlite3_column_count(this->stmt);
}

sqlite3_command::sqlite3_command(sqlite3_connection &con, const std::wstring &sql) :
con(con),
refs(0),
bind_index(1)
{
    const wchar_t *tail=NULL;
    if(sqlite3_prepare16(con.db, sql.data(), (int)sql.length()*2, &this->stmt, (const void**)&tail)!=SQLITE_OK)
        throw database_error(con);

    this->argc=sqlite3_column_count(this->stmt);
}

sqlite3_command::~sqlite3_command() {
    sqlite3_finalize(this->stmt);
}

void sqlite3_command::bind(int index) {
    if(sqlite3_bind_null(this->stmt, index)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, int data) {
    if(sqlite3_bind_int(this->stmt, index, data)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, long data) {
    if(sqlite3_bind_int(this->stmt, index, data)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, long long data) {
    if(sqlite3_bind_int64(this->stmt, index, data)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, double data) {
    if(sqlite3_bind_double(this->stmt, index, data)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, const char *data, int datalen) {
    if(sqlite3_bind_text(this->stmt, index, data, datalen, SQLITE_TRANSIENT)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, const wchar_t *data, int datalen) {
    if(sqlite3_bind_text16(this->stmt, index, data, datalen, SQLITE_TRANSIENT)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, const void *data, int datalen) {
    if(sqlite3_bind_blob(this->stmt, index, data, datalen, SQLITE_TRANSIENT)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, const std::string &data) {
    if(sqlite3_bind_text(this->stmt, index, data.data(), (int)data.length(), SQLITE_TRANSIENT)!=SQLITE_OK)
        throw database_error(this->con);
}

void sqlite3_command::bind(int index, const std::wstring &data) {
    if(sqlite3_bind_text16(this->stmt, index, data.data(), (int)data.length()*2, SQLITE_TRANSIENT)!=SQLITE_OK)
        throw database_error(this->con);
}

sqlite3_command & sqlite3_command::bind_i(void)
{
    bind(bind_index++);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(int data)
{
    bind(bind_index++,data);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(long data)
{
    bind(bind_index++,data);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(long long data)
{
    bind(bind_index++,data);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(double data)
{
    bind(bind_index++,data);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(const char *data, int datalen)
{
    bind(bind_index++,data,datalen);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(const wchar_t *data, int datalen)
{
    bind(bind_index++,data,datalen);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(const std::string & data)
{
    bind(bind_index++,data);
    return *this;
}

sqlite3_command & sqlite3_command::bind_i(const std::wstring & data)
{
    bind(bind_index++,data);
    return *this;
}

const char *sqlite3_command::sql_text(void){
    return sqlite3_sql(stmt);
}

sqlite3_reader sqlite3_command::execute_reader() {
    return sqlite3_reader(this);
}

void sqlite3_command::execute_nonquery() {
    this->execute_reader().read();
}

int sqlite3_command::execute_int() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_int(0);
}

long long sqlite3_command::execute_int64() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_int64(0);
}

double sqlite3_command::execute_double() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_double(0);
}

std::string sqlite3_command::execute_string() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_string(0);
}

std::wstring sqlite3_command::execute_string16() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_string16(0);
}

std::string sqlite3_command::execute_blob() {
    sqlite3_reader reader=this->execute_reader();
    if(!reader.read()) throw database_error("nothing to read");
    return reader.get_blob(0);
}

}
